#include "DrawScene.h"
#include "Scene.h"

#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define POS(a) MAX(a, 0)

using namespace CG;
using namespace LA;

// 根据类型创建并返回
DrawScene* DrawScene::Create(DrawType type)
{
	DrawScene* ds = nullptr;
	switch (type)
	{
	case DrawType::Draw_Axis:
	{
		ds = new DrawAxis;
		break;
	}
	case DrawType::Draw_Mesh:
	{
		ds = new DrawMesh;
		break;
	}
	case DrawType::Draw_Box:
	{
		ds = new DrawBox;
		break;
	}
	case DrawType::Draw_MeshData:
	{
		ds = new DrawMeshData;
		break;
	}
	case DrawType::Draw_CameraData:
	{
		ds = new DrawCameraData;
		break;
	}
	case DrawType::Draw_Tip:
	{
		ds = new DrawTip;
		break;
	}
	case DrawType::Draw_Sign:
	{
		ds = new DrawSign;
		break;
	}
	}
	return ds;
}

void DrawAxis::Draw(Scene* scene)
{
	// 开启虚线绘制
	glEnable(GL_LINE_STIPPLE);
	glLineStipple(1, 0xff);

	// 绘制坐标轴
	glColor3f(1, 0, 0);
	glBegin(GL_LINES);
	glVertex3f(0, 0, 0);
	glVertex3f(100, 0, 0);
	glEnd();

	glColor3f(0, 1, 0);
	glBegin(GL_LINES);
	glVertex3f(0, 0, 0);
	glVertex3f(0, 100, 0);
	glEnd();

	glColor3f(0, 0, 1);
	glBegin(GL_LINES);
	glVertex3f(0, 0, 0);
	glVertex3f(0, 0, 100);
	glEnd();

	// 根据坐标决定绘制间隔
	double dis = scene->m_camera->m_dis;
	int d = MAX(dis / 10, 1);

	// 沿着坐标轴绘制坐标值
	int i = 0;
	while (i < 100)
	{
		CString str;
		str.Format(L"%d", i);
		std::string s = ws2s(str.GetString());
		glColor3f(1, 0, 0);
		renderBitmapString(i, 0, 0, GLUT_BITMAP_HELVETICA_18, s);
		glColor3f(0, 1, 0);
		renderBitmapString(0, i, 0, GLUT_BITMAP_HELVETICA_18, s);
		glColor3f(0, 0, 1);
		renderBitmapString(0, 0, i, GLUT_BITMAP_HELVETICA_18, s);
		i += d;
	}

	// 重置颜色
	glColor3f(1, 1, 1);
	glDisable(GL_LINE_STIPPLE);
}

void DrawMesh::Draw(Scene* scene)
{
	// 设置显示模式
	glPolygonMode(scene->m_data.face, scene->m_data.mode);

	// 注意这几步不能合并
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_COLOR_ARRAY);

	// 计算网格光照
	for (auto it = scene->m_mList.begin(); it != scene->m_mList.end(); ++it)
	{
		// 首先清空颜色列表，然后不断叠加计算
		(*it)->m_cList.clear();
		for (auto lt = scene->m_lList.begin(); lt != scene->m_lList.end(); ++lt)
			scene->m_data.model->calculate(*it, *lt, scene->m_camera->m_viewer);
	}

	// 绘制所有网格
	for (int k = 0; k < scene->m_mList.size(); k++)
	{
		Mesh* mesh = scene->m_mList[k];

		// 按顺序标记顶点
		for (int i = 0; i < mesh->VertexNum(); i++)
			mesh->m_vList[i]->identity = i;

		// 输出所有顶点和颜色信息
		int count = mesh->VertexNum();
		float* arrayv = new float[count * 3];
		for (int i = 0; i < mesh->VertexNum(); i++)
		{
			arrayv[3 * i + 0] = mesh->m_vList[i]->pos.x;
			arrayv[3 * i + 1] = mesh->m_vList[i]->pos.y;
			arrayv[3 * i + 2] = mesh->m_vList[i]->pos.z;
		}
		// 注册顶点数组
		glVertexPointer(3, GL_FLOAT, 3 * sizeof(GLfloat), arrayv);

		// 注册颜色数组
		float* arrayc = new float[count * 3];
		for (int i = 0; i < mesh->VertexNum(); i++)
		{
			arrayc[3 * i + 0] = mesh->m_cList[i].r;
			arrayc[3 * i + 1] = mesh->m_cList[i].g;
			arrayc[3 * i + 2] = mesh->m_cList[i].b;
		}
		// 注册颜色数组
		glColorPointer(3, GL_FLOAT, 3 * sizeof(GLfloat), arrayc);

		// 遍历网格面，依次输出每个面
		for (int i = 0; i < mesh->FaceNum(); i++)
		{
			std::deque<vertex3f*> v = mesh->VertexOnFace(mesh->m_fList[i]);
			glBegin(GL_POLYGON);
			for (int j = 0; j < v.size(); j++)
				glArrayElement(v[j]->identity);
			glEnd();
		}
		// glArrayElement 会改变颜色为顶点颜色，这里需要重置
		glColor3f(1, 1, 1);

		delete[] arrayc;
		delete[] arrayv;
	}
}

void DrawBox::Draw(Scene* scene)
{
	// 没有选中，直接返回
	if (scene->m_data.choose == -1)
		return;

	bounding_box box = scene->m_mList[scene->m_data.choose]->m_box;

	glEnable(GL_LINE_STIPPLE);
	glLineStipple(1, 0xff);

	// 绘制 6 个面
	glBegin(GL_LINE_LOOP);
	glVertex3f(box.left, box.top, box.zFar);
	glVertex3f(box.left, box.bottom, box.zFar);
	glVertex3f(box.left, box.bottom, box.zNear);
	glVertex3f(box.left, box.top, box.zNear);
	glEnd();

	glBegin(GL_LINE_LOOP);
	glVertex3f(box.left, box.top, box.zNear);
	glVertex3f(box.left, box.bottom, box.zNear);
	glVertex3f(box.right, box.bottom, box.zNear);
	glVertex3f(box.right, box.top, box.zNear);
	glEnd();

	glBegin(GL_LINE_LOOP);
	glVertex3f(box.right, box.top, box.zNear);
	glVertex3f(box.right, box.bottom, box.zNear);
	glVertex3f(box.right, box.bottom, box.zFar);
	glVertex3f(box.right, box.top, box.zFar);
	glEnd();

	glBegin(GL_LINE_LOOP);
	glVertex3f(box.right, box.top, box.zFar);
	glVertex3f(box.right, box.bottom, box.zFar);
	glVertex3f(box.left, box.bottom, box.zFar);
	glVertex3f(box.left, box.top, box.zFar);
	glEnd();

	glBegin(GL_LINE_LOOP);
	glVertex3f(box.right, box.top, box.zFar);
	glVertex3f(box.left, box.top, box.zFar);
	glVertex3f(box.left, box.top, box.zNear);
	glVertex3f(box.right, box.top, box.zNear);
	glEnd();

	glBegin(GL_LINE_LOOP);
	glVertex3f(box.right, box.bottom, box.zFar);
	glVertex3f(box.right, box.bottom, box.zNear);
	glVertex3f(box.left, box.bottom, box.zNear);
	glVertex3f(box.left, box.bottom, box.zFar);
	glEnd();

	glDisable(GL_LINE_STIPPLE);
}

void DrawMeshData::Draw(Scene* scene)
{
	// 没有选中，不显示网格信息
	Mesh* mesh = nullptr;
	if (scene->m_data.choose != -1)
		mesh = scene->m_mList[scene->m_data.choose];

	// 切换为投影模式，保存原有的投影矩阵，然后重置为单位阵
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();

	// 应用二维正交投影，方便直接投影绘制文字
	CRect rect;
	scene->m_pWnd->GetClientRect(&rect);
	int width = rect.Width();
	int height = rect.Height();
	gluOrtho2D(0, width, 0, height);

	// 切换回模式视图，保存原有的视图矩阵，载入单位阵，这样就是直接映射
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();

	// 绘制字体，注意菜单也有高度占用
	int x = 10;
	std::string str = "Mesh: ";
	renderBitmapString(x, height - 40, 0, GLUT_BITMAP_HELVETICA_18, str);

	if (mesh != nullptr)
	{
		// 获得字符串宽度
		int w = getBitmapStringLength(GLUT_BITMAP_HELVETICA_18, str);
		str = mesh->m_name;
		renderBitmapString(x + w, height - 40, 0, GLUT_BITMAP_HELVETICA_18, str);
	}

	str = "Vertex: ";
	renderBitmapString(x, height - 60, 0, GLUT_BITMAP_HELVETICA_18, str);

	if (mesh != nullptr)
	{
		// 获得字符串宽度
		int w = getBitmapStringLength(GLUT_BITMAP_HELVETICA_18, str);
		CString cstr;
		cstr.Format(L"%d", mesh->VertexNum());
		str = ws2s(cstr.GetString());
		renderBitmapString(x + w, height - 60, 0, GLUT_BITMAP_HELVETICA_18, str);
	}

	str = "Face: ";
	renderBitmapString(x, height - 80, 0, GLUT_BITMAP_HELVETICA_18, str);

	if (mesh != nullptr)
	{
		// 获得字符串宽度
		int w = getBitmapStringLength(GLUT_BITMAP_HELVETICA_18, str);
		CString cstr;
		cstr.Format(L"%d", mesh->FaceNum());
		str = ws2s(cstr.GetString());
		renderBitmapString(x + w, height - 80, 0, GLUT_BITMAP_HELVETICA_18, str);
	}

	str = "Mesh type: ";
	renderBitmapString(x, height - 100, 0, GLUT_BITMAP_HELVETICA_18, str);

	if (mesh != nullptr)
	{
		// 获得字符串宽度
		int w = getBitmapStringLength(GLUT_BITMAP_HELVETICA_18, str);
		CString cstr;
		switch (mesh->m_type)
		{
		case MeshType::triangular: cstr = L"triangular"; break;
		case MeshType::rectangular: cstr = L"rectangular"; break;
		case MeshType::mixed: cstr = L"mixed"; break;
		}
		str = ws2s(cstr.GetString());
		renderBitmapString(x + w, height - 100, 0, GLUT_BITMAP_HELVETICA_18, str);
	}

	// 恢复原有的视图矩阵和投影矩阵
	glPopMatrix();
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();

	// 切换回视图模式
	glMatrixMode(GL_MODELVIEW);
}

void DrawCameraData::Draw(Scene* scene)
{
	// 切换为投影模式，保存原有的投影矩阵，然后重置为单位阵
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();

	// 应用二维正交投影，方便直接投影绘制文字
	CRect rect;
	scene->m_pWnd->GetClientRect(&rect);
	int width = rect.Width();
	int height = rect.Height();
	gluOrtho2D(0, width, 0, height);

	// 切换回模式视图，保存原有的视图矩阵，载入单位阵，这样就是直接映射
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();

	// 绘制字体，注意菜单也有高度占用
	int x = 10;
	renderBitmapString(x, height - 20, 0, GLUT_BITMAP_HELVETICA_18, "Camera: ");
	CString str;
	point3f pos = scene->m_camera->m_viewer;
	str.Format(L"(%f, %f, %f)", pos.x, pos.y, pos.z);

	// 获得字符串宽度
	int w = getBitmapStringLength(GLUT_BITMAP_HELVETICA_18, "Camera: ");
	renderBitmapString(x + w, height - 20, 0, GLUT_BITMAP_HELVETICA_18, ws2s(str.GetString()));

	// 恢复原有的视图矩阵和投影矩阵
	glPopMatrix();
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();

	// 切换回视图模式
	glMatrixMode(GL_MODELVIEW);
}

void DrawTip::Draw(Scene* scene)
{
	// 切换为投影模式，保存原有的投影矩阵，然后重置为单位阵
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();

	// 应用二维正交投影，方便直接投影绘制文字
	CRect rect;
	scene->m_pWnd->GetClientRect(&rect);
	int width = rect.Width();
	int height = rect.Height();
	gluOrtho2D(0, width, 0, height);

	// 切换回模式视图，保存原有的视图矩阵，载入单位阵，这样就是直接映射
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();

	// 绘制字体
	int x = 10;
	renderBitmapString(x, x, 0, GLUT_BITMAP_HELVETICA_18, ws2s(scene->m_data.tip.GetString()));

	// 恢复原有的视图矩阵和投影矩阵
	glPopMatrix();
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();

	// 切换回视图模式
	glMatrixMode(GL_MODELVIEW);
}

void DrawSign::Draw(Scene* scene)
{
	// 没有选中，不显示网格信息
	if (scene->m_data.choose == -1 || scene->m_data.vId == -1)
		return;

	Mesh* mesh = scene->m_mList[scene->m_data.choose];
	vertex3f* v = mesh->m_vList[scene->m_data.vId];
	renderBitmapString(v->pos.x, v->pos.y, v->pos.z, GLUT_BITMAP_HELVETICA_18, "0");

	// 获得相邻顶点和半边进行标记
	std::deque<half_edge*> eList = mesh->AdjacentHalfEdge(v);
	std::deque<vertex3f*> vList = mesh->AdjacentVertex(v);

	// 在半边中点绘制标记
	for (int i = 0; i < eList.size(); i++)
	{
		half_edge* e = eList[i];
		point3f p = linear(e->v->pos, mesh->Destination(e)->pos, 0.5);
		renderBitmapString(p.x, p.y, p.z, GLUT_BITMAP_HELVETICA_18, "1");
	}

	for (int i = 0; i < vList.size(); i++)
	{
		vertex3f* tv = vList[i];
		renderBitmapString(tv->pos.x, tv->pos.y, tv->pos.z, GLUT_BITMAP_HELVETICA_18, "2");
	}
}